iT邦幫忙

1

從 Linux 基礎實現 Docker Bridge 網路:一步步理解容器通訊 (1)

  • 分享至 

  • xImage
  •  

部落格好讀版


前言

近期在閱讀《Docker 實戰 6堂課:56個實驗動手做,掌握 Linux 容器核心技術》這本書,書中涵蓋了 Docker 的基礎知識、容器技術、網路設定及 Linux 核心概念。透過學習這本書,我深入了解了許多以前未曾完整研究過的 Linux 知識,特別是在 Linux 網路處理的部分。因此,我希望藉由這次機會,記錄自己的操作,並用我自己的理解來解釋其中的邏輯和知識點。

目標

以下是專案的主要目標:完全模擬在 Docker 上建立兩個容器間的 bridge 網路

  • ns0 的網路請求必須能透過 veth0,經由 docker1 傳送到 ns1,反之亦然。
  • ns0 和 ns1 必須要能連到外網。

準備環境

我在 AWS EC2 啟用了兩個實例,network-test-1network-test-2network-test-1 作為主機進行各種實驗和設置,network-test-2 則用來模擬和驗證對外的網路情境,以幫助更全面地測試配置效果,特別是跨主機的網路連通性。

以下所有操作皆在 network-test-1 上進行。

建立 net namespace 與 bridge

當 Docker 容器使用 bridge 類型網路時,會為每個容器建立一個虛擬網路介面,並將這些網路介面放入專屬的 net namespace。這樣可以確保每個容器的網路環境互相隔離,從而增強安全性和靈活性。net namespace 是 Linux 提供的一種隔離機制,可以讓不同的網路空間彼此獨立。

首先,我們建立兩個 net namespace 來模擬容器的網路隔離:

$ sudo ip netns add ns0
$ sudo ip netns add ns1

查看 network namespace:

$ ip netns list
---
ns1
ns0

新增名為 docker1 的橋接網路(bridge network):

$ sudo ip link add docker1 type bridge
# 查看網路介面
$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
    altname eni-0cb768586b1414a54
    altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff

建立 veth pair

接下來,我們將建立兩對 veth pair(虛擬網路介面對),並將它們分別與 docker1 配對。這些 veth pair 將用於在不同的 net namespace 之間建立虛擬連接,模擬容器之間的網路通訊:

$ sudo ip link add veth0 type veth peer name veth0-br
$ sudo ip link add veth1 type veth peer name veth1-br

查詢當前的網路介面清單:

$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
    altname eni-0cb768586b1414a54
    altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
4: veth0-br@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff
5: veth0@veth0-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 9a:0e:80:cc:ac:53 brd ff:ff:ff:ff:ff:ff
6: veth1-br@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff
7: veth1@veth1-br: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 62:31:3a:d4:65:b9 brd ff:ff:ff:ff:ff:ff

現在,我們要把 veth0-brveth1-br 網路介面與 docker1 這個 bridge 連接起來,並且將 veth0 移動到 ns0veth1 移動到 ns1。這一步是為了確保 ns0ns1 之間的網路流量可以通過橋接網路 docker1,從而模擬容器之間的通訊。

veth0-br 為例,將 veth0-br 加入到橋接網路介面 docker1 中,讓 veth0-br 成為橋接網路 docker1 的一部分,就像將多條網線插入同一個交換機中:

  • 任何通過 veth0-br 的流量會被橋接到 docker1,就像數據包進入了交換機。
  • 連接到同一橋接網路的其他介面也可以與 veth0-br 通訊,就像交換機內的網線可以互相通信。

可以將 docker1 想像成一個虛擬的交換機,負責將不同網路介面之間的數據包互相轉發。

# 移動網路介面到指定 ns
$ sudo ip link set veth0 netns ns0
$ sudo ip link set veth1 netns ns1
# 將 veth0-br, veth1-br 添加到 docker1
$ sudo ip link set veth0-br master docker1
$ sudo ip link set veth1-br master docker1

查詢當前的網路介面清單:

$ ip link list
---
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 06:9d:02:88:52:9d brd ff:ff:ff:ff:ff:ff
    altname eni-0cb768586b1414a54
    altname device-number-0.0
3: docker1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fa:b6:c0:55:33:71 brd ff:ff:ff:ff:ff:ff
4: veth0-br@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 1000
    link/ether 7e:56:1a:0a:5c:8b brd ff:ff:ff:ff:ff:ff link-netns ns0
6: veth1-br@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop master docker1 state DOWN mode DEFAULT group default qlen 1000
    link/ether 32:65:8e:22:01:80 brd ff:ff:ff:ff:ff:ff link-netns ns1

由於 veth0veth1 已經被移動到其他 net namespace 當中,所以不會出現在當前的清單中。還留著的 veth0-brveth1-br,其屬性中顯示了剛剛設定的 master docker1

設置 IP 與啟動網路介面

接下來,我們要為 veth0veth1 設定 IP:

$ sudo ip netns exec ns0 ip addr add 172.18.0.2/24 dev veth0
$ sudo ip netns exec ns1 ip addr add 172.18.0.3/24 dev veth1

ip netns exec ns0 這個指令的前綴,意思是進入 ns0 執行後面的指令,Kubernetes 也有類似的用法。

啟動所有的網路介面:

$ sudo ip netns exec ns0 ip link set veth0 up
$ sudo ip netns exec ns1 ip link set veth1 up
$ sudo ip link set veth0-br up
$ sudo ip link set veth1-br up
$ sudo ip link set docker1 up

查看網路介面的狀態。我們可以使用指令 ip addrip link。為了方便查看,對 ip 指令使用 -br flag(brief 的縮寫),讓輸出結果更簡潔:

# 查詢 root namespace
$ ip -br link
---
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP>
enX0             UP             06:9d:02:88:52:9d <BROADCAST,MULTICAST,UP,LOWER_UP>
docker1          UP             fa:b6:c0:55:33:71 <BROADCAST,MULTICAST,UP,LOWER_UP>
veth0-br@if5     UP             7e:56:1a:0a:5c:8b <BROADCAST,MULTICAST,UP,LOWER_UP>
veth1-br@if7     UP             32:65:8e:22:01:80 <BROADCAST,MULTICAST,UP,LOWER_UP>
# 查詢 ns0 namespace
$ sudo ip netns exec ns0 ip -br addr
lo               DOWN
veth0@if4        UP             172.18.0.2/24 fe80::980e:80ff:fecc:ac53/64
# 查詢 ns1 namespace
$ sudo ip netns exec ns1 ip -br addr
lo               DOWN
veth1@if6        UP             172.18.0.3/24 fe80::6031:3aff:fed4:65b9/64

驗證網路連線

理論上,現在已經打通了 veth0veth1 之間的連線,來測試一下:

# 從 ns1 ping 位於 ns0 的 veth0 IP
$ sudo ip netns exec ns1 ping -c 2 172.18.0.2
---
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.077 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.046 ms

--- 172.18.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1052ms

所以,我們完成了嗎?

乍看之下,我們已經完成了第一個目標,但實際上在安裝 Docker 後,Docker 會自動修改 iptables 和 NAT 規則,這可能導致我們手動配置的網路連線出現衝突或無法正常運作的情況。因此,在有 Docker 的環境中,上述的網路連線設定可能無法正常運作。下一篇將探討這些挑戰,並試著解決這些問題,以確保網路配置的穩定性和預期行為。

參考


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
jazzinfo
iT邦新手 5 級 ‧ 2024-12-11 08:40:31

請問圖片中標示 ns1 與 ns2, 說明文章是 ns0 與 ns1,是圖示標錯了嗎?

是的,你真細心🙂
已經修正了,感謝你的指正

我要留言

立即登入留言